package cn.t.facade.android.engine.cabinet;


import cn.t.constants.CommonConstants;
import cn.t.constants.ParamConstants;
import cn.t.dto.device.CargowaySkuDTO;
import cn.t.dto.device.DeviceUpstreamDO;
import cn.t.dto.device.OpenCloseWeightVO;
import cn.t.dto.device.vo.OpenCloseDTO;
import cn.t.dto.device.vo.QtyVO;
import cn.t.dto.device.vo.WeightVO;
import cn.t.dto.order.ReplenishmentDetailDTO;
import cn.t.dto.order.android.OverPickupDetailDTO;
import cn.t.dto.order.android.PickupDetailDTO;
import cn.t.enums.ActionTypeEnum;
import cn.t.enums.device.DeviceTypeEnum;
import cn.t.enums.order.ErrorTypeEnum;
import cn.t.model.device.DeviceDataDO;
import cn.t.model.order.OverPickNumDO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author l
 * @create 2024/7/1 19:25
 */
@Slf4j
@Component
public class WeightOpenCabinetStrategy extends GenericCabinetHandlingHandler {

    /**
     * 处理参数异常并且返回开门数据
     *
     * @param model 关门请求参数
     * @return 开门数据
     */
    public List<WeightVO> checkData(OpenCloseDTO model) {
        if (ObjectUtils.isEmpty(model.getScale())) {
            return null;
        }
        //校验开门数据
        List<DeviceDataDO> dataByOrderSn = deviceDataFacade.findDataByOrderSn(model,model.getOptionTypeEnum(),null);
        Optional<DeviceDataDO> deviceDataDO = dataByOrderSn.stream().filter(dataDO -> dataDO.getAction().equals(ActionTypeEnum.open.getCode())).findFirst();

        if (!deviceDataDO.isPresent()) {

            return null;
        }
        DeviceDataDO deviceDataOld = deviceDataDO.get();

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        List<WeightVO> openScale = new ArrayList<>();
        try {
            DeviceUpstreamDO deviceUpstreamData = objectMapper.readValue(deviceDataOld.getDeviceData(), new TypeReference<DeviceUpstreamDO>() {
            });
            openScale = deviceUpstreamData.getScale();

        } catch (JsonProcessingException e) {
            log.warn("数据转换异常 checkData {}{}", model.getDeviceId(), e.getMessage());

            return null;
        }
        return openScale;
    }

    @Override
    public void weightCount(OpenCloseDTO model) {
        //数据校验
        List<WeightVO> deviceDataDO = checkData(model);
        if (deviceDataDO != null) {
            //获取第一条开门数据
            OpenCloseWeightVO openCloseWeightVO = new OpenCloseWeightVO();
            openCloseWeightVO.setCloseScale(new ArrayList<>(model.getScale()));

            openCloseWeightVO.setOpenScale(deviceDataDO);

            openCloseWeightVO.setCargowayMap( model.getCargowaySkuMap());
            List<WeightVO> deviceDataDON = deviceDataDO.stream().filter(weightVO -> weightVO.getWeight() != null && weightVO.getNo() != null).collect(Collectors.toList());
            model.setOpenScale(deviceDataDON.stream().collect(Collectors.toMap(WeightVO::getNo, WeightVO::getWeight)));
            model.setScaleMap(model.getScale().stream().collect(Collectors.toMap(WeightVO::getNo, WeightVO::getWeight)));
            //重力计算 openCloseWeightVO
            List<QtyVO> qtys = deviceCommunicationFacade.calcGravity(openCloseWeightVO);
            List<QtyVO> qtyList =qtys .stream().filter(QtyVO::isWeightData).collect(Collectors.toList());
            if (qtyList.size() > 0) {
                //重量抖动没有有效数据
                // 判断是否全部为无效数据 数量为-1 and 5以内
                boolean isErr = qtyList.stream().allMatch(qtyVO -> CommonConstants.MINUS_ONE.equals(qtyVO.getQty()) && BigDecimal.valueOf(qtyVO.getWeight()).abs().compareTo(BigDecimal.valueOf(CommonConstants.FIVE)) < 0);
                if (isErr) {
                   qtyList=new ArrayList<>();
                }
                model.setQtyList(qtyList);
            }
        }
    }

    @Override
    public void addException(OpenCloseDTO model) {
        exceptionLogFacade.addException(model);
    }


    public void replenishmentErrLog(Map<Integer, ReplenishmentDetailDTO> detailMap, OpenCloseDTO openCloseDO) {
        Map<Integer, Double> scaleWeightMap = openCloseDO.getScaleMap();
        Map<Integer, Double> openWeightMap = openCloseDO.getOpenScale();
        for (QtyVO qtyVO : openCloseDO.getQtyList()) {
            CargowaySkuDTO cargowaySkuDTO = openCloseDO.getCargowaySkuMap().get(qtyVO.getNo());
            ReplenishmentDetailDTO replenishmentDetailDTO = detailMap.get(qtyVO.getNo());
            Double  closeWeights= scaleWeightMap.get(qtyVO.getNo());

            ErrorTypeEnum errorTypeEnum = checkErr(cargowaySkuDTO, qtyVO, replenishmentDetailDTO, closeWeights,openWeightMap.get(qtyVO.getNo()));
            qtyVO.setErrorTypeEnum(errorTypeEnum);
        }
    }





    @Override
    public List<OverPickupDetailDTO> responseOverPickup(OverPickNumDO overPickNumDO, List<PickupDetailDTO.DetailVO> detailVOS) {
        if (ObjectUtils.isEmpty(overPickNumDO.getOverPickupList())){
            return new ArrayList<>();
        }
        Map<String, List<PickupDetailDTO.DetailVO>> skuCodeMap = detailVOS.stream().collect(Collectors.groupingBy(PickupDetailDTO.DetailVO::getSkuCode));
        return overPickNumDO.getOverPickupList().stream().map(overPickupRecordDO ->{
                    List<PickupDetailDTO.DetailVO> details = skuCodeMap.get(overPickupRecordDO.getSkuCode());
                    OverPickupDetailDTO detailVO = new OverPickupDetailDTO();
                    detailVO.setSku(overPickupRecordDO.getSkuCode());
                    detailVO.setAmount(overPickupRecordDO.getOverQty());
                    detailVO.setCargowayNoStr(details.stream().map(PickupDetailDTO.DetailVO::getCargowayNo).collect(Collectors.joining(",")));
                    detailVO.setSkuName(details.get(0).getSkuName());
                    detailVO.setSkuImg(details.get(0).getSkuImg());

                   return detailVO;
        }
               ).collect(Collectors.toList());
    }


    public void pickErrLog(OpenCloseDTO openCloseDO) {
        Map<Integer, CargowaySkuDTO> cargowaySkuMap = openCloseDO.getCargowaySkuMap();
        Map<Integer, Double> scaleWeight = openCloseDO.getScaleMap();
        Map<Integer, Double> openScale = openCloseDO.getOpenScale();
        //根据类型异常判断
        for (QtyVO qtyVO : openCloseDO.getQtyList()) {
            ErrorTypeEnum errorTypeEnum = pickCheckErr(cargowaySkuMap.get(qtyVO.getNo()), qtyVO, scaleWeight.get(qtyVO.getNo()),openScale.get(qtyVO.getNo()));
            qtyVO.setErrorTypeEnum(errorTypeEnum);
        }
    }


    @Override
    public String getDeviceProcessType() {
        return ParamConstants.CABINET_TYPE + DeviceTypeEnum.GRAVITY_DOOR_CABINET.getCode();
    }




    /**
     * 问题1：#判断$goodsId为空，则记录异常$errorList且continue：无商品货道重量减少，goodsId：
     * 回答1：$goodsId是通过cargoway_id查询r_cargoway_goods表的记录，这个判断不会进，前面已经有了查询返回记录非空判断
     * <p>
     * 问题2：#判断$pickupNum超出误差范围则记录异常$errorList且continue：超出误差范围，pickupNum：
     * 回答2：将根据后台配置计算出商品允许的总差异重量与实际称重结果除以单重得到的余数，差值大于5，则提示以上报错
     * <p>
     * 问题3：#查询r_cargoway_goods数据，为空则记录异常$errorList且continue：货道未上架商品,cargoway_id：
     * 回答3：同问题1
     * <p>
     * 问题4：#判断两次重量数据数量if($cargowayNum != $afterCargowayNum)则记录异常$errorList且continue：货道数据错乱不匹配
     * 回答4：不是数量对比，是根据排序后的同位置的货道号对比，比如：
     * $a[0]['no']=1;
     * $b[0]['no']=2;
     * 这种情况就会报错
     * <p>
     * 问题5：#计算补货前后重量差：$diffData = $data - $newData;
     * 回答5：$data;#开门商品重量
     * $newData;#关门商品重量
     * $diffData = $data - $newData;#重量差
     * <p>
     * 问题6：#判断重量差异大于5，则记录异常$errorList且continue：误差允许范围内：
     * 回答6：$diffWeight<=5则报错
     */
    public ErrorTypeEnum checkErr(CargowaySkuDTO cargowaySkuDTO, QtyVO qtyVO, ReplenishmentDetailDTO detail,Double scaleWeight, Double openWeight) {
        ErrorTypeEnum validate = validate(cargowaySkuDTO, qtyVO, scaleWeight,openWeight);
        if (validate != null) {
            return validate;
        }
        if (ObjectUtils.isEmpty(detail) && qtyVO.getWeight() > 0 && qtyVO.getQty() > 0) {
            //非计划补货货道增重
            return ErrorTypeEnum.ERROR21;
        }
        if (qtyVO.getWeight() < 0) {
            //补货货道重量减少
            return ErrorTypeEnum.ERROR20;
        }
        if (qtyVO.getQty() == -1) {
            //超出误差范围
            return ErrorTypeEnum.ERROR24;
        }
        if (cargowaySkuDTO.getInventory() + qtyVO.getQty() <= 0) {
            //补货货道货品库存为负数
            return ErrorTypeEnum.ERROR12;
        }
        return null;
    }

    /**
     * 计算前验证
     */
    public ErrorTypeEnum validate(CargowaySkuDTO cargowaySkuDTO, QtyVO qtyVO, Double scaleWeight,Double openWeight) {
        //正确结果为正整数和0
        if (cargowaySkuDTO == null) {
            //货道未上架商品
            return ErrorTypeEnum.ERROR22;
        }
        if (cargowaySkuDTO.getSkuWeight() == null || cargowaySkuDTO.getSkuWeight().compareTo(BigDecimal.ZERO) == 0) {
            return ErrorTypeEnum.ERROR8;
        }
        if (ObjectUtils.isEmpty(scaleWeight) ) {
            //关门后未传重量数据
            return ErrorTypeEnum.ERROR16;
        }
        if (ObjectUtils.isEmpty(qtyVO.getQty()) || ObjectUtils.isEmpty(qtyVO.getWeight())) {
            //货道数据错乱不匹配
            //重力数量未获取到
            return ErrorTypeEnum.ERROR23;
        }
        double five = -CommonConstants.FIVE;
        //货道硬件损坏
        if (qtyVO.isWeightData() &&  (scaleWeight < five || openWeight < five)) {
            //货道硬件损坏
            return ErrorTypeEnum.ERROR4;
        }


        return null;
    }

    public ErrorTypeEnum pickCheckErr(CargowaySkuDTO cargowaySkuDTO, QtyVO qtyVO,  Double scaleWeight, Double openWeight) {
        ErrorTypeEnum validate = validate(cargowaySkuDTO, qtyVO, scaleWeight,openWeight);
        if (validate != null) {
            return validate;
        }
        Integer qty = qtyVO.getQty();
        if (qtyVO.getWeight() >=0) {
            //货道增重
            return ErrorTypeEnum.ERROR7;
        }
        if (cargowaySkuDTO.getInventory()<= 0 && qty>0) {
            //零库存领取
            return ErrorTypeEnum.ERROR9;
        }
        if (qty == -1) {
            //超出误差范围
            return ErrorTypeEnum.ERROR6;
        }
        return null;
    }
    public List<PickupDetailDTO.DetailVO> responsePickup(OpenCloseDTO openCloseDO) {
        return openCloseDO.getQtyList().stream().filter(qtyVO -> qtyVO.getErrorTypeEnum() == null).map(qtyVO -> {
            PickupDetailDTO.DetailVO pickupDetailInfo = cargowaySkuDTOConverter.do2PickupDetail(openCloseDO.getCargowaySkuMap().get(qtyVO.getNo()));
            if(pickupDetailInfo==null){
                pickupDetailInfo=new PickupDetailDTO.DetailVO();
            }
            //领用应该是重量减少所以是整数，但是重量增加
            if (ObjectUtils.isEmpty(qtyVO.getWeight()) && ObjectUtils.isEmpty(qtyVO.getQty()) && qtyVO.getWeight() > (double) 0 && qtyVO.getQty() > 0) {
                //放入
                pickupDetailInfo.setQty(qtyVO.getQty() * -1);
            } else {
                //领用
                pickupDetailInfo.setQty(qtyVO.getQty());
            }
            if (qtyVO.getErrorTypeEnum()!=null){
                pickupDetailInfo.setStatus(qtyVO.getErrorTypeEnum().getName());
            }

            return pickupDetailInfo;
        }).sorted(Comparator.comparing(PickupDetailDTO.DetailVO::getCargowayNo)).collect(Collectors.toList());
    }
}
